use crate::db::entities::task::Model;
use crate::db::entities::task_knowledge;
use crate::db::{db_knowledge, db_knowledge_progress, db_task, db_task_knowledge};
use crate::rest_api::api_knowledge::{self, Knowledge};
use crate::rest_api::api_task::{CreateParams, SearchParams, Task, UpdateParams};
use crate::rest_api::service::srv_knowledge::knowledge_from_model;
use crate::rest_api::service::srv_scheduler::validate_scheduler_id;

use anyhow::Result;
use log::warn;

use rbase::define::{ApiError, BaseError};
use rbase::id_utils::{opt_str2id, str2id};
use rbase_iam::db::define::SYSTEM_ID;
use rbase_iam::rest_api::api_user::UserToken;
use rbase_iam::rest_api::service::srv_tag;

pub async fn task_from_model(task_model: &Model) -> Result<Task> {
    let task = Task::from_model(task_model);
    Ok(task)
}

pub async fn find_task_by_id(user_token: &UserToken, task_id: &String) -> Result<Task, ApiError> {
    let task_model = db_task::find_by_id_and_owner_ids(str2id(task_id)?, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(task_id.to_string()))?;

    Ok(task_from_model(&task_model).await?)
}

pub async fn delete_task_by_id(user_token: &UserToken, task_id: &String) -> Result<(), ApiError> {
    let task_model = db_task::find_by_id_and_owner_ids(str2id(task_id)?, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(task_id.to_string()))?;
    // 普通用户不能修改系统资源
    if !user_token.is_admin && task_model.owner_id == Some(SYSTEM_ID) {
        return Err(BaseError::PermissionDenied.into());
    }
    db_task::delete_by_id(task_model.id).await?;
    Ok(())
}

pub async fn update_task_by_id(user_token: &UserToken, task_id: &String, new_task: &UpdateParams) -> Result<(), ApiError> {
    let old_model = db_task::find_by_id_and_owner_ids(str2id(task_id)?, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(task_id.to_string()))?;
    // 普通用户不能修改系统资源
    if !user_token.is_admin && old_model.owner_id == Some(SYSTEM_ID) {
        return Err(BaseError::PermissionDenied.into());
    }
    let new_model = Model {
        id: old_model.id,
        name: new_task.name.clone(),
        status: new_task.status,
        ..Default::default()
    };
    db_task::update(new_model).await?;
    Ok(())
}

pub async fn create_task(user_token: &UserToken, new_task: &CreateParams) -> Result<Task, ApiError> {
    let scheduler_id = match validate_scheduler_id(user_token, &Some(new_task.scheduler_id.to_string())).await? {
        Some(scheduler_id) => scheduler_id,
        None => return Err(BaseError::InvalidRequest.into()),
    };

    let new_model = Model {
        name: Some(new_task.name.clone()),
        scheduler_id: Some(scheduler_id),
        owner_id: Some(user_token.id),
        ..Default::default()
    };

    let created_model = db_task::insert(new_model).await?;

    for knowledge_id in &new_task.knowledge_ids {
        let knowledge_id = str2id(knowledge_id)?;
        let mut progress = match db_knowledge_progress::find_by_scheduler_id_and_knowledge_id(scheduler_id, knowledge_id).await? {
            Some(process) => process,
            None => {
                warn!(
                    "Knowledge progress not found for scheduler ID {} and knowledge ID {}",
                    scheduler_id, knowledge_id
                );
                continue;
            }
        };

        db_task_knowledge::insert_task_knowledge(task_knowledge::Model {
            task_id: created_model.id,
            knowledge_id,
            ..Default::default()
        })
        .await?;

        let count = match progress.learned_count {
            Some(count) => count,
            None => 1,
        };

        progress.learned_count = Some(count);
        db_knowledge_progress::update(progress).await?;
    }

    Ok(task_from_model(&created_model).await?)
}

pub async fn search(user_token: &UserToken, task_search_params: &SearchParams) -> Result<(Vec<Task>, i64), ApiError> {
    let (tasks, total) = db_task::search(&db_task::SearchParams {
        scheduler_id: match &task_search_params.scheduler_id {
            Some(id) => Some(str2id(id)?),
            None => None,
        },
        name: task_search_params.name.clone(),
        offset: task_search_params.offset,
        limit: task_search_params.limit,
        order_by: task_search_params.order_by.clone(),
        order: task_search_params.order.clone(),
        owner_ids: user_token.owner_ids.clone(),
    })
    .await?;

    let mut task_results = Vec::new();
    for task in tasks {
        task_results.push(task_from_model(&task).await?);
    }

    Ok((task_results, total))
}

pub async fn search_task_knowledge(
    user_token: &UserToken,
    task_id: &String,
    search_params: &api_knowledge::SearchParams,
) -> Result<(Vec<Knowledge>, i64), ApiError> {
    let tag_ids = srv_tag::validate_tag_ids(user_token, &search_params.tag_ids).await?;
    let (knowledges, total) = db_knowledge::search_by_task_id(
        str2id(task_id)?,
        &db_knowledge::SearchParams {
            name: search_params.name.clone(),
            category_id: opt_str2id(&search_params.category_id)?,
            subject_id: opt_str2id(&search_params.subject_id)?,
            offset: search_params.offset,
            limit: search_params.limit,
            order_by: search_params.order_by.clone(),
            order: search_params.order.clone(),
            cols: search_params.cols.clone(),
            tag_ids,
            owner_ids: user_token.owner_ids.clone(),
        },
    )
    .await?;
    let mut knowledge_results = Vec::new();
    for knowledge in knowledges {
        knowledge_results.push(knowledge_from_model(&knowledge).await?);
    }

    Ok((knowledge_results, total))
}
